#region Copyright (c) 2003, newtelligence AG. All rights reserved.
/*
// Copyright (c) 2003, newtelligence AG. (http://www.newtelligence.com)
// Original BlogX Source Code: Copyright (c) 2003, Chris Anderson (http://simplegeek.com)
// All rights reserved.
//  
// Redistribution and use in source and binary forms, with or without modification, are permitted 
// provided that the following conditions are met: 
//  
// (1) Redistributions of source code must retain the above copyright notice, this list of 
// conditions and the following disclaimer. 
// (2) Redistributions in binary form must reproduce the above copyright notice, this list of 
// conditions and the following disclaimer in the documentation and/or other materials 
// provided with the distribution. 
// (3) Neither the name of the newtelligence AG nor the names of its contributors may be used 
// to endorse or promote products derived from this software without specific prior 
// written permission.
//      
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS 
// OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 
// AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 
// IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 
// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// -------------------------------------------------------------------------
//
// Original BlogX source code (c) 2003 by Chris Anderson (http://simplegeek.com)
// 
// newtelligence is a registered trademark of newtelligence Aktiengesellschaft.
// 
// For portions of this software, the some additional copyright notices may apply 
// which can either be found in the license.txt file included in the source distribution
// or following this notice. 
//
*/
#endregion

using System;
using System.Collections;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using ICSharpCode.SharpZipLib.Checksums;
using ICSharpCode.SharpZipLib.Zip;

namespace newtelligence.DasBlog.Runtime
{
	/// <summary>
	/// 
	/// </summary>
	public interface ILoggingDataService
	{
		/// <summary>
		/// 
		/// </summary>
		/// <param name="date"></param>
		/// <returns></returns>
		LogDataItemCollection GetReferralsForDay(DateTime date);

		/// <summary>
		/// 
		/// </summary>
		/// <param name="logItem"></param>
		void AddReferral(LogDataItem logItem);

		/// <summary>
		/// 
		/// </summary>
		/// <param name="date"></param>
		/// <returns></returns>
		LogDataItemCollection GetClickThroughsForDay(DateTime date);

		/// <summary>
		/// 
		/// </summary>
		/// <param name="logItem"></param>
		void AddClickThrough(LogDataItem logItem);

		/// <summary>
		/// 
		/// </summary>
		/// <param name="date"></param>
		/// <returns></returns>
		LogDataItemCollection GetAggregatorBugHitsForDay(DateTime date);

		/// <summary>
		/// 
		/// </summary>
		/// <param name="logItem"></param>
		void AddAggregatorBugHit(LogDataItem logItem);

		/// <summary>
		/// 
		/// </summary>
		/// <param name="date"></param>
		/// <returns></returns>
		LogDataItemCollection GetCrosspostReferrersForDay(DateTime date);

		/// <summary>
		/// 
		/// </summary>
		/// <param name="logItem"></param>
		void AddCrosspostReferrer(LogDataItem logItem);

		/// <summary>
		/// 
		/// </summary>
		/// <param name="date"></param>
		/// <returns></returns>
		EventDataItemCollection GetEventsForDay(DateTime date);

		/// <summary>
		/// 
		/// </summary>
		/// <param name="eventData"></param>
		void AddEvent(EventDataItem eventData);
	}

	/// <summary>
	/// 
	/// </summary>
	public class LoggingDataServiceFactory
	{
		private static Hashtable services = new Hashtable();

		/// <summary>
		/// 
		/// </summary>
		/// <param name="logLocation"></param>
		/// <returns></returns>
		public static ILoggingDataService GetService(string logLocation)
		{
			ILoggingDataService service;

			lock (services.SyncRoot)
			{
				service = services[logLocation.ToUpper()] as ILoggingDataService;
				if (service == null)
				{
					service = new LoggingDataServiceXml(logLocation);
					services.Add(logLocation.ToUpper(), service);
				}
			}
			return service;
		}
	}

	public class LogEncoder
	{
		public static string Encode( string input)
		{
			if (input != null)
			{
				return input.Replace("<", "&lt;").Replace(">", "&gt;");
			}
			else
			{
				return null;
			}
		}

		public static string EncodeBadTags( string input)
		{
			if (input != null)
			{
				Regex regex = new Regex("<(?!span|/span|br/|br|/br|p|/p|a|/a)([^\"]+?|.+?\".*?\".*?)>", RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.Singleline);
				
				foreach (Match match in regex.Matches(input))
				{
					input = input.Replace(match.Value, match.Value.Replace("<", "&lt;").Replace(">", "&gt;"));
				}

				return input;
			}
			else
			{
				return null;
			}
		}
	}

	internal class LoggingDataServiceXml : ILoggingDataService
	{
		private ReaderWriterLock referralReaderLock = new ReaderWriterLock();
		private ReaderWriterLock clickThroughReaderLock = new ReaderWriterLock();
		private ReaderWriterLock aggregatorBugHitReaderLock = new ReaderWriterLock();
		private ReaderWriterLock crosspostReferrerReaderLock = new ReaderWriterLock();
		private ReaderWriterLock eventReaderLock = new ReaderWriterLock();
		private string logDirectory;

		internal LoggingDataServiceXml(string loggingLocation)
		{
			logDirectory = loggingLocation;
			if (!Directory.Exists(logDirectory))
			{
				throw new ArgumentException(String.Format("Invalid directory {0}", logDirectory), "loggingLocation");
			}
		}

		private void ArchiveLogFiles(DateTime dt)
		{
			// archive event log
			ThreadPool.QueueUserWorkItem(new WaitCallback(ArchiveLogFileWorker), GetEventLogPath(dt));

			// archive referral log
			ThreadPool.QueueUserWorkItem(new WaitCallback(ArchiveLogFileWorker), GetReferralLogPath(dt));

			// click through log
			ThreadPool.QueueUserWorkItem(new WaitCallback(ArchiveLogFileWorker), GetClickThroughLogPath(dt));

			// aggregator bug log
			ThreadPool.QueueUserWorkItem(new WaitCallback(ArchiveLogFileWorker), GetAggregatorBugHitLogPath(dt));

			// cross post referrer log
			ThreadPool.QueueUserWorkItem(new WaitCallback(ArchiveLogFileWorker), GetCrosspostReferrerLogPath(dt));
		}

		private void ArchiveLogFileWorker(object argument)
		{
			string oldPath = argument as String;

			if (File.Exists(oldPath))
			{
				try
				{
					ZipOutputStream s = new ZipOutputStream(File.Create(oldPath + ".zip"));
					s.SetLevel(6); // 0 - store only to 9 - means best zipion
					Crc32 crc = new Crc32();

					using (FileStream fs = File.OpenRead(oldPath))
					{
						byte[] buffer = new byte[fs.Length];
						fs.Read(buffer, 0, buffer.Length);
						ZipEntry entry = new ZipEntry(Path.GetFileName(oldPath));
						entry.DateTime = DateTime.Now;
						entry.Size = fs.Length;
						crc.Reset();
						crc.Update(buffer);

						entry.Crc = crc.Value;
						s.PutNextEntry(entry);
						s.Write(buffer, 0, buffer.Length);
					}

					s.Finish();
					s.Close();

					File.Delete(oldPath);
				}
				catch (Exception e)
				{
					ErrorTrace.Trace(TraceLevel.Error, e);
				}
			}
		}

		private string GetReferralLogPath(DateTime dateUtc)
		{
			return Path.Combine(logDirectory, dateUtc.Date.ToString("yyyy-MM-dd") + ".referrer.log");
		}

		private string GetClickThroughLogPath(DateTime dateUtc)
		{
			return Path.Combine(logDirectory, dateUtc.Date.ToString("yyyy-MM-dd") + ".clickthrough.log");
		}

		private string GetAggregatorBugHitLogPath(DateTime dateUtc)
		{
			return Path.Combine(logDirectory, dateUtc.Date.ToString("yyyy-MM-dd") + ".aggbug.log");
		}

		private string GetCrosspostReferrerLogPath(DateTime dateUtc)
		{
			return Path.Combine(logDirectory, dateUtc.Date.ToString("yyyy-MM-dd") + ".crosspost.log");
		}

		private string GetEventLogPath(DateTime dateUtc)
		{
			return Path.Combine(logDirectory, dateUtc.Date.ToString("yyyy-MM-dd") + ".events.log");
		}

		private string GetLogText(string path)
		{
			string result = "";

			if (path.EndsWith(".zip"))
			{
				ZipInputStream s = new ZipInputStream(File.OpenRead(path));

				while (s.GetNextEntry() != null)
				{
					int size = 2048;
					byte[] data = new byte[2048];

					StringBuilder sb = new StringBuilder();

					while (true)
					{
						size = s.Read(data, 0, data.Length);

						if (size > 0)
						{
							sb.Append(new ASCIIEncoding().GetString(data, 0, size));
						}
						else
						{
							break;
						}
					}

					result = sb.ToString();
					break;
				}
			}
			else
			{
				using (StreamReader sw = new StreamReader(path))
				{
					try
					{
						result = sw.ReadToEnd();
					}
					catch (Exception e)
					{
						ErrorTrace.Trace(TraceLevel.Error, e);
					}
				}
			}

			return result;
		}

		private string GetReferralLogText(DateTime dateUtc)
		{
			string result = "";

			referralReaderLock.AcquireReaderLock(250);

			try
			{
				// check for the zip version
				if (File.Exists(Path.Combine(logDirectory, dateUtc.Date.ToString("yyyy-MM-dd") + ".referrer.log.zip")))
				{
					result = this.GetLogText(
						Path.Combine(logDirectory, dateUtc.Date.ToString("yyyy-MM-dd") + ".referrer.log.zip"));
				}
				else
				{
					result = this.GetLogText(GetReferralLogPath(dateUtc));
				}

			}
			catch (Exception e)
			{
				ErrorTrace.Trace(TraceLevel.Error, e);
			}
			finally
			{
				referralReaderLock.ReleaseLock();
			}

			return result;
		}

		private void WriteReferral(LogDataItem logItem)
		{
			try
			{
				using (StreamWriter sw = new StreamWriter(GetReferralLogPath(logItem.RequestedUtc), true))
				{
					try
					{
						sw.WriteLine("e2\ttime\t{0}\turl\t{1}\turlreferrer\t{2}\tuseragent\t{3}", logItem.RequestedUtc.ToString("s"), logItem.UrlRequested, logItem.UrlReferrer, logItem.UserAgent);
					}
					catch (Exception e)
					{
						ErrorTrace.Trace(TraceLevel.Error, e);
					}
				}
			}
			catch (Exception e)
			{
				ErrorTrace.Trace(TraceLevel.Error, e);
			}
		}

		private void AddReferralWorker(object argument)
		{
			try
			{
				LogDataItem logItem = argument as LogDataItem;
				referralReaderLock.AcquireWriterLock(250);
				try
				{
					WriteReferral(logItem);
				}
				catch (Exception e)
				{
					ErrorTrace.Trace(TraceLevel.Error, e);
				}
				finally
				{
					referralReaderLock.ReleaseLock();
				}
			}
			catch (Exception e)
			{
				ErrorTrace.Trace(TraceLevel.Error, e);
			}
		}

		LogDataItemCollection ILoggingDataService.GetReferralsForDay(DateTime dateUtc)
		{
			LogDataItemCollection logs = new LogDataItemCollection();

			string log = GetReferralLogText(dateUtc);
			Regex parser = new Regex(@"e2\ttime\t(?<time>.*)\turl\t(?<url>.*)\turlreferrer\t(?<urlreferrer>.*)\tuseragent\t(?<useragent>.*)");
			MatchCollection matches = parser.Matches(log);
			if (matches != null)
			{
				foreach (Match match in matches)
				{
					LogDataItem item = new LogDataItem();
					item.RequestedUtc = DateTime.Parse(match.Groups["time"].Value);
					item.UrlRequested = match.Groups["url"].Value;
					item.UrlReferrer = match.Groups["urlreferrer"].Value;
					item.UserAgent = match.Groups["useragent"].Value;
					logs.Add(item);
				}
			}

			return logs;
		}

		void ILoggingDataService.AddReferral(LogDataItem logItem)
		{
			ThreadPool.QueueUserWorkItem(new WaitCallback(AddReferralWorker), logItem);
		}

		private string GetClickThroughLogText(DateTime dateUtc)
		{
			string result = "";

			clickThroughReaderLock.AcquireReaderLock(250);

			try
			{
				// check for the zip version
				if (File.Exists(Path.Combine(logDirectory, dateUtc.Date.ToString("yyyy-MM-dd") + ".clickthrough.log.zip")))
				{
					result = this.GetLogText(
						Path.Combine(logDirectory, dateUtc.Date.ToString("yyyy-MM-dd") + ".clickthrough.log.zip"));
				}
				else
				{
					result = this.GetLogText(GetClickThroughLogPath(dateUtc));
				}
			}
			catch (Exception e)
			{
				ErrorTrace.Trace(TraceLevel.Error, e);
			}
			finally
			{
				clickThroughReaderLock.ReleaseLock();
			}

			return result;
		}

		private void WriteClickThrough(LogDataItem logItem)
		{
			try
			{
				using (StreamWriter sw = new StreamWriter(GetClickThroughLogPath(logItem.RequestedUtc), true))
				{
					try
					{
						sw.WriteLine("c1\ttime\t{0}\turl\t{1}\turlreferrer\t{2}\tuseragent\t{3}", logItem.RequestedUtc.ToString("s"), logItem.UrlRequested, logItem.UrlReferrer, logItem.UserAgent);
					}
					catch (Exception e)
					{
						ErrorTrace.Trace(TraceLevel.Error, e);
					}
				}
			}
			catch (Exception e)
			{
				ErrorTrace.Trace(TraceLevel.Error, e);
			}
		}

		private void AddClickThroughWorker(object argument)
		{
			try
			{
				LogDataItem logItem = argument as LogDataItem;
				referralReaderLock.AcquireWriterLock(250);
				try
				{
					WriteClickThrough(logItem);
				}
				catch (Exception e)
				{
					ErrorTrace.Trace(TraceLevel.Error, e);
				}
				finally
				{
					referralReaderLock.ReleaseLock();
				}
			}
			catch (Exception e)
			{
				ErrorTrace.Trace(TraceLevel.Error, e);
			}
		}

		LogDataItemCollection ILoggingDataService.GetClickThroughsForDay(DateTime dateUtc)
		{
			LogDataItemCollection logs = new LogDataItemCollection();

			string log = GetClickThroughLogText(dateUtc);
			Regex parser = new Regex(@"c1\ttime\t(?<time>.*)\turl\t(?<url>.*)\turlreferrer\t(?<urlreferrer>.*)\tuseragent\t(?<useragent>.*)");
			MatchCollection matches = parser.Matches(log);
			if (matches != null)
			{
				foreach (Match match in matches)
				{
					LogDataItem item = new LogDataItem();
					item.RequestedUtc = DateTime.Parse(match.Groups["time"].Value);
					item.UrlRequested = match.Groups["url"].Value;
					item.UrlReferrer = match.Groups["urlreferrer"].Value;
					item.UserAgent = match.Groups["useragent"].Value;
					logs.Add(item);
				}
			}

			return logs;
		}

		void ILoggingDataService.AddClickThrough(LogDataItem logItem)
		{
			ThreadPool.QueueUserWorkItem(new WaitCallback(AddClickThroughWorker), logItem);
		}

		private string GetAggregatorBugHitLogText(DateTime dateUtc)
		{
			string result = "";

			aggregatorBugHitReaderLock.AcquireReaderLock(250);

			try
			{
				// check for the zip version
				if (File.Exists(Path.Combine(logDirectory, dateUtc.Date.ToString("yyyy-MM-dd") + ".aggbug.log.zip")))
				{
					result = this.GetLogText(
						Path.Combine(logDirectory, dateUtc.Date.ToString("yyyy-MM-dd") + ".aggbug.log.zip"));
				}
				else
				{
					result = this.GetLogText(GetAggregatorBugHitLogPath(dateUtc));
				}
			}
			catch (Exception e)
			{
				ErrorTrace.Trace(TraceLevel.Error, e);
			}
			finally
			{
				aggregatorBugHitReaderLock.ReleaseLock();
			}

			return result;
		}

		private void WriteAggregatorBugHit(LogDataItem logItem)
		{
			try
			{
				using (StreamWriter sw = new StreamWriter(GetAggregatorBugHitLogPath(logItem.RequestedUtc), true))
				{
					try
					{
						sw.WriteLine("c1\ttime\t{0}\turl\t{1}\turlreferrer\t{2}\tuseragent\t{3}", logItem.RequestedUtc.ToString("s"), logItem.UrlRequested, logItem.UrlReferrer, logItem.UserAgent);
					}
					catch (Exception e)
					{
						ErrorTrace.Trace(TraceLevel.Error, e);
					}
				}
			}
			catch (Exception e)
			{
				ErrorTrace.Trace(TraceLevel.Error, e);
			}
		}

		private void AddAggregatorBugHitWorker(object argument)
		{
			try
			{
				LogDataItem logItem = argument as LogDataItem;
				referralReaderLock.AcquireWriterLock(250);
				try
				{
					WriteAggregatorBugHit(logItem);
				}
				catch (Exception e)
				{
					ErrorTrace.Trace(TraceLevel.Error, e);
				}
				finally
				{
					referralReaderLock.ReleaseLock();
				}
			}
			catch (Exception e)
			{
				ErrorTrace.Trace(TraceLevel.Error, e);
			}
		}

		LogDataItemCollection ILoggingDataService.GetAggregatorBugHitsForDay(DateTime dateUtc)
		{
			LogDataItemCollection logs = new LogDataItemCollection();

			string log = GetAggregatorBugHitLogText(dateUtc);
			Regex parser = new Regex(@"c1\ttime\t(?<time>.*)\turl\t(?<url>.*)\turlreferrer\t(?<urlreferrer>.*)\tuseragent\t(?<useragent>.*)");
			MatchCollection matches = parser.Matches(log);
			if (matches != null)
			{
				foreach (Match match in matches)
				{
					LogDataItem item = new LogDataItem();
					item.RequestedUtc = DateTime.Parse(match.Groups["time"].Value);
					item.UrlRequested = match.Groups["url"].Value;
					item.UrlReferrer = match.Groups["urlreferrer"].Value;
					item.UserAgent = match.Groups["useragent"].Value;
					logs.Add(item);
				}
			}

			return logs;
		}

		void ILoggingDataService.AddAggregatorBugHit(LogDataItem logItem)
		{
			ThreadPool.QueueUserWorkItem(new WaitCallback(AddAggregatorBugHitWorker), logItem);
		}

		private string GetCrosspostReferrerLogText(DateTime dateUtc)
		{
			string result = "";

			crosspostReferrerReaderLock.AcquireReaderLock(250);

			try
			{
				// check for the zip version
				if (File.Exists(Path.Combine(logDirectory, dateUtc.Date.ToString("yyyy-MM-dd") + ".crosspost.log.zip")))
				{
					result = this.GetLogText(
						Path.Combine(logDirectory, dateUtc.Date.ToString("yyyy-MM-dd") + ".crosspost.log.zip"));
				}
				else
				{
					result = this.GetLogText(GetCrosspostReferrerLogPath(dateUtc));
				}
			}
			catch (Exception e)
			{
				ErrorTrace.Trace(TraceLevel.Error, e);
			}
			finally
			{
				crosspostReferrerReaderLock.ReleaseLock();
			}

			return result;
		}

		private void WriteCrosspostReferrer(LogDataItem logItem)
		{
			try
			{
				using (StreamWriter sw = new StreamWriter(GetCrosspostReferrerLogPath(logItem.RequestedUtc), true))
				{
					try
					{
                        sw.WriteLine("c1\ttime\t{0}\turl\t{1}\turlreferrer\t{2}\tuseragent\t{3}", 
                            logItem.RequestedUtc.ToString("s"), 
                            logItem.UrlRequested, 
                            logItem.UrlReferrer,
                            logItem.UserAgent );
					}
					catch (Exception e)
					{
						ErrorTrace.Trace(TraceLevel.Error, e);
					}
				}
			}
			catch (Exception e)
			{
				ErrorTrace.Trace(TraceLevel.Error, e);
			}
		}

		private void AddCrosspostReferrerWorker(object argument)
		{
			try
			{
				LogDataItem logItem = argument as LogDataItem;
				referralReaderLock.AcquireWriterLock(250);
				try
				{
					WriteCrosspostReferrer(logItem);
				}
				catch (Exception e)
				{
					ErrorTrace.Trace(TraceLevel.Error, e);
				}
				finally
				{
					referralReaderLock.ReleaseLock();
				}
			}
			catch (Exception e)
			{
				ErrorTrace.Trace(TraceLevel.Error, e);
			}
		}

		LogDataItemCollection ILoggingDataService.GetCrosspostReferrersForDay(DateTime dateUtc)
		{
			LogDataItemCollection logs = new LogDataItemCollection();

			string log = GetCrosspostReferrerLogText(dateUtc);
			Regex parser = new Regex(@"c1\ttime\t(?<time>.*)\turl\t(?<url>.*)\turlreferrer\t(?<urlreferrer>.*)\tuseragent\t(?<useragent>.*)");
			MatchCollection matches = parser.Matches(log);
			if (matches != null)
			{
				foreach (Match match in matches)
				{
					LogDataItem item = new LogDataItem();
					item.RequestedUtc = DateTime.Parse(match.Groups["time"].Value);
					item.UrlRequested = match.Groups["url"].Value;
					item.UrlReferrer = match.Groups["urlreferrer"].Value;
					item.UserAgent = match.Groups["useragent"].Value;
					logs.Add(item);
				}
			}

			return logs;
		}

		void ILoggingDataService.AddCrosspostReferrer(LogDataItem logItem)
		{
			ThreadPool.QueueUserWorkItem(new WaitCallback(AddCrosspostReferrerWorker), logItem);
		}

		private string GetEventLogText(DateTime dateUtc)
		{
			string result = "";

			eventReaderLock.AcquireReaderLock(250);

			try
			{
				// check for the zip version
				if (File.Exists(Path.Combine(logDirectory, dateUtc.Date.ToString("yyyy-MM-dd") + ".events.log.zip")))
				{
					result = this.GetLogText(
						Path.Combine(logDirectory, dateUtc.Date.ToString("yyyy-MM-dd") + ".events.log.zip"));
				}
				else
				{
					result = this.GetLogText(GetEventLogPath(dateUtc));
				}
			}
			catch (Exception e)
			{
				ErrorTrace.Trace(TraceLevel.Error, e);
			}
			finally
			{
				eventReaderLock.ReleaseLock();
			}

			return result;
		}

		private void WriteEvent(EventDataItem logItem)
		{
			try
			{
				if (!File.Exists(GetEventLogPath(logItem.EventTimeUtc)))
				{
					this.ArchiveLogFiles(logItem.EventTimeUtc.AddDays(-1));
				}

				using (StreamWriter sw = new StreamWriter(GetEventLogPath(logItem.EventTimeUtc), true))
				{
					try
					{
						string htmlMessage = logItem.HtmlMessage.Replace("\n", "");
						sw.WriteLine("l2\ttime\t{0}\tcode\t{1}\tmessage\t{2}", logItem.EventTimeUtc.ToString("s"), logItem.EventCode, htmlMessage);
					}
					catch (Exception e)
					{
						ErrorTrace.Trace(TraceLevel.Error, e);
					}
				}
			}
			catch (Exception e)
			{
				ErrorTrace.Trace(TraceLevel.Error, e);
			}
		}

		private void AddEventWorker(object argument)
		{
			try
			{
				EventDataItem logItem = argument as EventDataItem;
				eventReaderLock.AcquireWriterLock(250);
				try
				{
					WriteEvent(logItem);
				}
				catch (Exception e)
				{
					ErrorTrace.Trace(TraceLevel.Error, e);
				}
				finally
				{
					eventReaderLock.ReleaseLock();
				}
			}
			catch (Exception e)
			{
				ErrorTrace.Trace(TraceLevel.Error, e);
			}
		}

		void ILoggingDataService.AddEvent(EventDataItem eventData)
		{
			ThreadPool.QueueUserWorkItem(new WaitCallback(AddEventWorker), eventData);
		}

		EventDataItemCollection ILoggingDataService.GetEventsForDay(DateTime dateUtc)
		{
			EventDataItemCollection events = new EventDataItemCollection();

			string log = GetEventLogText(dateUtc);
			Regex parser = new Regex(@"l2\ttime\t(?<time>.*)\tcode\t(?<code>.*)\tmessage\t(?<message>.*)");
			MatchCollection matches = parser.Matches(log);
			if (matches != null)
			{
				foreach (Match match in matches)
				{
					EventDataItem item = new EventDataItem();
					item.EventTimeUtc = DateTime.Parse(match.Groups["time"].Value);
					item.EventCode = Int32.Parse(match.Groups["code"].Value);
					item.HtmlMessage = match.Groups["message"].Value;
					events.Add(item);
				}
			}

			return events;
		}
	}
}